package com.neu.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.neu.base.AuthContants;
import com.neu.base.Result;
import com.neu.entity.security.LoginUser;
import com.neu.service.AuthenticationService;
import com.neu.utils.JwtUtils;
import com.neu.utils.RedisUtil;
import com.neu.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class UsernamePasswordFilter extends UsernamePasswordAuthenticationFilter {

    private RedisUtil redisUtil;
    private JwtUtils jwtUtils;
    private AuthenticationService authenticationService;

    public UsernamePasswordFilter(AuthenticationManager authenticationManager, RedisUtil redisUtil, JwtUtils jwtUtils, AuthenticationService authenticationService) {
        this.redisUtil = redisUtil;
        this.jwtUtils = jwtUtils;
        this.authenticationService = authenticationService;
        super.setAuthenticationManager(authenticationManager);
        super.setPostOnly(true);//只支持post请求
        super.setFilterProcessesUrl("/auth/login");
        super.setUsernameParameter("username");
        super.setPasswordParameter("password");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("执行这个方法");
        //2.获取用户名和密码 从请求体中拿出来 使用ObjectMapper进行反序列化
        try {
            LoginUser user = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
            String username = user.getUsername();
            String password = user.getPassword();
            System.out.println("用户名: " + username + " 密码: " + password);
            //将请求信息封装到Authentication对象  principal: 用户信息   credentials: 密码   authorities: 权限
            //[Principal=888, Credentials=[PROTECTED], Authenticated=false, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            setDetails(request, authRequest);

            // 调用AuthenticationManager的authenticate方法 进行身份验证
            return this.getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 登录成功后的处理 此时再生成token并存入redis
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("登录成功，开始处理信息");
        //获取用户信息 从Authentication对象中获取
        LoginUser userPrincipal = (LoginUser) authResult.getPrincipal();
        String username = userPrincipal.getUsername();
        //获取完整的用户信息
        LoginUser loginUser = authenticationService.login(username);
        log.info("用户信息: " + loginUser);
        String token = jwtUtils.generateToken(username);
        log.info("生成的token: " + token);
        //将token放入响应头中
        response.setHeader(AuthContants.TOKEN_HEADER, token);
        //将token存入redis
        redisUtil.set(AuthContants.REDIS_KEY_AUTH_TOKEN + username, token, jwtUtils.getExpiration());
        //将用户信息存入redis
        redisUtil.set(AuthContants.REDIS_KEY_AUTH_USER_DETAIL + username, loginUser, jwtUtils.getExpiration());
        //将用户信息存入上下文 principal: 用户信息   credentials: 密码   authorities: 权限
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, loginUser.getPassword(), loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        log.info("存储的上下文"+SecurityContextHolder.getContext().getAuthentication().toString());
        //返回成功信息
        ServletUtils.renderResult(response, Result.success(token));

    }

    /**
     * 如果 attemptAuthentication 抛出 AuthenticationException 则会调用这个方法
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException {
        log.info("登录失败: {}", failed.getMessage());
        ServletUtils.renderResult(response, Result.error(failed.getMessage()));

    }


}
